﻿using Autofac;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
using Quartz;
using Quartz.Impl;
using Serilog;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection.Metadata;
using System.Text;
using System.Threading.Tasks;
using WCS_Entity.Model;
using WCS_Entity.Prorocol;
using WCS_Quartz_Task;
using WCS_Service.DomianService.Interface;
using WCS_Service.DomianService.Models;
using WCS_Service.Enums;
using WCS_Service.Interface;
using WCS_Service.Models;
using WCS_Service.ProtocolServices.Interface;
using WCS_Utility.ConstString;
using WCS_Utility.Extensions;
using WCS_Utility.Helper;

namespace WCS_Service.DomianService.ComDomainService
{
    /// <summary>
    /// Tcp通信服务
    /// </summary>
    public class TcpComDomainManage : IComBaseDomainManage
    {
        private readonly IComponentContext _componentContext;

        public IProtocolService protocolService { get; set; }

        //通信基础参数
        public string comPara { get; set; }
        //协议配置报文
        public List<ProtocolEntity> protocolPara { get; set; } = new List<ProtocolEntity>();
        public string serviceName { get; set; }
        public int autoEquipmentId { get; set; }
        public string equipmentType { get; set; }
        public string areaCode { get; set; }
        //协议内容
        public ConcurrentDictionary<string, ProtocolModel> protocolMsgList { get; set; } = new ConcurrentDictionary<string, ProtocolModel>();
        private string gengricMsg = "";
        //接收事件
        public event Func<string, Task> ReciveChange;
        //断开连接事件
        public event Func<ClientSideDisConnectEventArgs, Task> ComDisConnectEvent;

        private TcpComParaModel tcpComParaModel = new TcpComParaModel();
        protected Encoding Encoding { get; private set; } = Encoding.UTF8;
        private bool isConnect = false;
        private bool isClose = false;
        public TcpClient TcpClient { get; private set; }
        

        private string heartBeatMsg = ConstStringData.HeartBeatMsg;
        private byte[] ReadBytes = new byte[1024];

        private readonly IQuartzJobProcess _quartzJobProcess;

        public TcpComDomainManage(IComponentContext componentContext, IQuartzJobProcess quartzJobProcess)
        {
            _componentContext = componentContext;
            _quartzJobProcess = quartzJobProcess;
        }

        public async Task<bool> ConnectAsync()
        {
            if (isConnect) return true;
            try
            {
                if (!await InitService())
                {
                    return false;
                }
                if (string.IsNullOrEmpty(tcpComParaModel.ip) || tcpComParaModel.port == 0)
                {
                    Log.Error($"通信参数错误!");
                    return false;
                }
                if (TcpClient is not null)
                {
                    TcpClient.Close();
                }
                TcpClient = new TcpClient();
                TcpClient.BeginConnect(tcpComParaModel.ip, tcpComParaModel.port, ConnectChecker, null);
                Log.Information($"{tcpComParaModel.ip}-连接成功");
                isConnect = true;
                //处理协议内容
                var initFlag = await InitProtocolMsgAsync();
                if (!initFlag)
                {
                    await DisConnectAsync();
                    return false;
                }
                InitProtocolServiceAsync();
                ////启动心跳
                await SendHeartbeatAsync();
                
                return true;
            }
            catch (Exception ex)
            {
                Log.Error($"{tcpComParaModel.ip}-连接失败-{ex.Message}");
                return false;
            }
        }
        /// <summary>
        /// 发送心跳
        /// </summary>
        /// <returns></returns>
        public async Task SendHeartbeatAsync()
        {
            var jobDetail = JobBuilder.Create<OpticalJob>().WithIdentity(ConstStringData.OpticalHeartbeat + autoEquipmentId, 
                    ConstStringData.OpticalHeartbeat).Build();
            jobDetail.JobDataMap.Put("sendHeart", SendHeartbeat);
            var trigger = TriggerBuilder.Create()
                                        .WithSimpleSchedule(m =>
                                        {
                                            m.WithIntervalInSeconds(20).RepeatForever();
                                        })
                                        .Build();

            //添加心跳任务
            await _quartzJobProcess.AddJobAsync(jobDetail, trigger);
        }
        /// <summary>
        /// 发送心跳包
        /// </summary>
        private async Task SendHeartbeat()
        {
            try
            {
                if (!isConnect)
                {
                    return;
                }
                var flag = await SendAsync(heartBeatMsg);
                ///发送失败说明连接异常
                if (!flag)
                {
                    await _quartzJobProcess.DeleteJobAsync(ConstStringData.OpticalHeartbeat + autoEquipmentId, ConstStringData.OpticalHeartbeat);
                    await DisConnectAsync();
                }
            }
            catch (Exception ex)
            {
                Log.Error($"心跳发送异常{ex.Message}");
                return;
            }
        }


        public async Task DisConnectAsync()
        {
            try
            {
                if (!isConnect) return;

                if (TcpClient != null)
                {
                    TcpClient.Dispose();
                    TcpClient.Close();
                    TcpClient = null;
                    isConnect = false;
                }
                ComDisConnectEvent?.Invoke(new ClientSideDisConnectEventArgs(DisConnectReason.ServerDown, equipmentType, autoEquipmentId));
                Log.Information($"连接关闭");
            }
            catch (Exception ex)
            {
                Log.Error($"关闭异常{ex.Message}");
            }
        }
        public async void ReceiveAsync(IAsyncResult ar)
        {
            try
            {
                if (TcpClient is null) return;
                int len = TcpClient.GetStream().EndRead(ar);
                if (len > 0)
                {
                    ReciveChange?.Invoke(Encoding.UTF8.GetString(ReadBytes));
                    ReadBytes = new byte[1024];
                    TcpClient.GetStream().BeginRead(ReadBytes, 0, ReadBytes.Length, ReceiveAsync, null);
                }
                else
                {
                    await ConnectAsync();
                }
            }
            catch (IOException ex)
            {
                Log.Error($"IO异常{ex.Message}");
                await ConnectAsync();
            }
            catch (Exception ex)
            {
                Log.Error($"接收异常{ex.Message}");
                await ConnectAsync();
            }
        }
        
        public async Task<bool> SendAsync(string para)
        {
            try
            {
                if (TcpClient is null) return false;
                byte[] data = Encoding.GetBytes(para);
                await TcpClient.GetStream().WriteAsync(data, 0, data.Length);
                return true;
            }
            catch (IOException ex)
            {
                Log.Error($"IO异常{ex.Message}");
                await ConnectAsync();
                return false;
            }
            catch (Exception ex)
            {
                Log.Error($"发送异常{ex.Message}");
                await ConnectAsync();
                return false;
            }
        }

        #region private
        /// <summary>
        /// 初期化协议服务
        /// </summary>
        /// <returns></returns>
        private void InitProtocolServiceAsync()
        {
            if (protocolService is null)
            {
                return;
            }
            //ReciveChange += protocolService.ReceiveMsgAsync;
        }
        /// <summary>
        /// 连接判断
        /// </summary>
        /// <returns></returns>
        private void ConnectChecker(IAsyncResult ar)
        {
            if (isClose || TcpClient is null)
            {
                return;
            }
            if (TcpClient.Connected == false)
            {
                TcpClient.Close();
                TcpClient = new TcpClient();
                TcpClient.BeginConnect(tcpComParaModel.ip, tcpComParaModel.port, ConnectChecker, null);
            }
            else
            {
                TcpClient.EndConnect(ar);
                //读取数据
                TcpClient.GetStream().BeginRead(ReadBytes, 0, ReadBytes.Length, ReceiveAsync, null);
            }
        }

        private async Task<bool> InitProtocolMsgAsync()
        {
            var serialNetInfo = protocolPara.FirstOrDefault(p => p.isSerialNet);
            if (serialNetInfo is not null)
            {
                return await SerialNetMsgProcessAsync();
            }
            //协议通用字段处理
            var gengricInfo = protocolPara.Where(p => string.IsNullOrEmpty(p.messageType))?.ToList();
            if (gengricInfo is not null && gengricInfo.Any())
            {
                await GengricMsgProcessAsync(gengricInfo);
            }
            var msgInfo = protocolPara.Where(m => !string.IsNullOrEmpty(m.messageType))?.GroupBy(msg => msg.messageType).ToList();
            if (msgInfo is null)
            {
                return false;
            }
            List<Task<bool>> processFlagList = new List<Task<bool>>();
            msgInfo.ForEach(info =>
            {
                processFlagList.Add(ProtocolMsgProcessAsync(info));
            });
            await Task.WhenAll(processFlagList);
            var flags = processFlagList.Select(p => p.Result).ToList();
            foreach (var item in flags)
            {
                if (!item)
                {
                    return false;
                }
            }
            return true;
        }
        /// <summary>
        /// 透传报文的处理
        /// </summary>
        /// <returns></returns>
        private async Task<bool> SerialNetMsgProcessAsync()
        {
            return await Task.Run(() => 
            {
                if (protocolPara.Count != 1)
                {
                    Log.Error($"Protocol内容配置错误，透传类型报文只需配置一条!");
                    return false;
                }
                var protocolInfo = protocolPara.FirstOrDefault();
                if (protocolInfo is null)
                {
                    Log.Error($"Protocol内容获取失败!");
                    return false;
                }
                if (!_componentContext.TryResolveKeyed<IProtocolService>(protocolInfo.protocolType,out IProtocolService protocolModel))
                {
                    Log.Error($"protocol协议类型不支持!");
                    return false;
                }
                if (protocolModel is null)
                {
                    Log.Error($"protovolService获取失败!");
                    return false;
                }
                protocolModel.isSerialNet = true;
                protocolModel.equipmentId = autoEquipmentId;
                protocolService = protocolModel;
                return true;
            });
        }

        private async Task GengricMsgProcessAsync(List<ProtocolEntity> infos)
        {
            await Task.Run(() =>
            {
                var infoList = infos.OrderBy(info => info.fieldIndex);
                int preIndex = 0;
                StringBuilder msg = new StringBuilder();
                foreach (var info in infoList)
                {
                    if (CommonHelper.ContinuousNumberChecker(preIndex, info.fieldIndex, out int differenceNum))
                    {
                        msg.Append(info.fieldInfo + ',');
                        preIndex++;
                    }
                    else
                    {
                        msg.Append(CommonHelper.GetInsertChar(differenceNum - 1, ConstStringData.InsertStringInfo));
                        msg.Append(info.fieldInfo + ',');
                        preIndex += differenceNum - 1;
                    }
                }
                gengricMsg = msg.ToString();
            });
        }

        /// <summary>
        /// 协议报文处理
        /// </summary>
        /// <param name="infos"></param>
        /// <returns></returns>
        private async Task<bool> ProtocolMsgProcessAsync(IGrouping<string, ProtocolEntity> infos)
        {
            if (!infos.Any())
            {
                return false;
            }
            List<Task<ProtocolModel>> dataMsg = new List<Task<ProtocolModel>>();

            //根据报文发送者分组
            var senderMsgList = infos.GroupBy(i => i.sender).ToList();
            senderMsgList.ForEach(s =>
            {
                dataMsg.Add(ProcotolMsgSenderProcessAsync(infos.Key, s.Key, s));
            });
            await Task.WhenAll(dataMsg);
            var protocolList = dataMsg.Select(s => s.Result)?.ToList();

            protocolList?.ForEach(p =>
            {
                protocolMsgList.TryAdd(string.Join('-', infos.Key, p.sender), p);
            });
            return true;
        }
        private async Task<ProtocolModel> ProcotolMsgSenderProcessAsync(string msgType, string sender, IGrouping<string, ProtocolEntity> infos)
        {
            ProtocolModel protocolModel = new ProtocolModel();
            protocolModel.sender = sender;
            //protocolModel.functionCode = infos.Where(i => i.isFunctionCode).FirstOrDefault()?.fieldInfo.Split(":").Last();

            var dataMsgList = infos.GroupBy(i => i.fieldIndex).ToList();
            List<Task<MsgDataInfoModel>> dataMsgInfos = new List<Task<MsgDataInfoModel>>();
            dataMsgList.ForEach(d =>
            {
                dataMsgInfos.Add(ProtocolMsgSubProcessAsync(d));
            });
            await Task.WhenAll(dataMsgInfos);
            var dainfoList = dataMsgInfos.Select(d => d.Result).OrderBy(d => d.insertIndex).ToList();

            protocolModel.msgDataInfo = MontageMsgInfo(dainfoList);

            return protocolModel;
        }
        /// <summary>
        /// 合并在一帧数据中同一索引下的所有数据
        /// </summary>
        /// <param name="infos"></param>
        /// <returns></returns>
        private async Task<MsgDataInfoModel> ProtocolMsgSubProcessAsync(IGrouping<int, ProtocolEntity> infos)
        {
            MsgDataInfoModel result = new MsgDataInfoModel();
            List<Task<MsgDataInfoModel>> dataInfoList = new List<Task<MsgDataInfoModel>>();
            foreach (var item in infos)
            {
                //dataInfoList.Add(DataInfoProcess(item.isBitAnalysis, item.fieldInfo, infos.Key));
            }
            await Task.WhenAll(dataInfoList);
            var dataInfo = dataInfoList.Select(d => d.Result).OrderBy(i => i.insertIndex).ToList();

            result.insertIndex = infos.Key;
            dataInfo.ForEach(d =>
            {
                result.info += d.info;
            });
            return result;
        }
        /// <summary>
        /// 报文内容处理
        /// </summary>
        /// <param name="isBitAnalysis"></param>
        /// <param name="info"></param>
        /// <param name="startIndex"></param>
        /// <returns></returns>
        private async Task<MsgDataInfoModel> DataInfoProcess(bool isBitAnalysis, string info, int startIndex)
        {
            MsgDataInfoModel result = new MsgDataInfoModel();
            await Task.Run(() =>
            {
                if (!info.Contains(":"))
                {
                    result.insertIndex = startIndex;
                    result.info = info;
                }
                else
                {
                    result = isBitAnalysis ? BitInfoProcess(info, startIndex) : InfoProcess(info, startIndex);
                }
            });
            return result;
        }
        /// <summary>
        /// 按位有效报文内容处理
        /// </summary>
        /// <param name="info"></param>
        /// <param name="startIndex"></param>
        /// <returns></returns>
        private MsgDataInfoModel BitInfoProcess(string info, int startIndex)
        {
            MsgDataInfoModel msgDataInfoModel = new MsgDataInfoModel();

            var index = info.Substring(0, info.IndexOf(':'));
            if (index is null)
            {
                Log.Error("数据异常");
                return null;
            }
            var bitInfos = info.Substring(info.IndexOf(':') + 1).Split(new char[] { '{', ':', '}' }, StringSplitOptions.RemoveEmptyEntries);
            if (bitInfos is null || !BitInfoCheck(bitInfos, out var bitMsg))
            {
                Log.Error("位数据异常");
                return null;
            }
            var insertIndex = index.StringToIndex();
            if (!insertIndex.HasValue)
            {
                Log.Error("数据索引异常! ");
                return null;
            }
            msgDataInfoModel.insertIndex = startIndex + insertIndex.Value;
            msgDataInfoModel.info = bitMsg;
            return msgDataInfoModel;
        }

        private MsgDataInfoModel InfoProcess(string info, int startIndex)
        {
            MsgDataInfoModel msgDataInfoModel = new MsgDataInfoModel();
            var dataInfos = info.Split(':').ToList();
            if (dataInfos is null || dataInfos.Count != 2)
            {
                Log.Error("数据异常! ");
                return null;
            }
            var index = dataInfos.First().StringToIndex();
            if (!index.HasValue)
            {
                Log.Error("数据索引异常! ");
                return null;
            }
            msgDataInfoModel.insertIndex = index.Value + startIndex;
            msgDataInfoModel.info = dataInfos.LastOrDefault();
            return msgDataInfoModel;
        }
        private bool BitInfoCheck(string[] info, out string bitMsg)
        {
            bitMsg = "";
            foreach (var item in info)
            {
                if (item.Length != 4)
                {
                    return false;
                }
            }
            bitMsg = string.Join(null, info).BinaryToHex();
            return true;
        }
        /// <summary>
        /// 拼接报文内容
        /// </summary>
        /// <param name="infos"></param>
        /// <returns></returns>
        private string MontageMsgInfo(List<MsgDataInfoModel> infos)
        {
            string gengricInfo = gengricMsg;
            string result = "";
            if (string.IsNullOrEmpty(gengricMsg))
            {
                var msgInfos = infos.Select(i => i.info).ToList();
                result = string.Join(null, msgInfos);
                return result;
            }
            var msgInfoList = gengricInfo.Split(',').ToList();
            for (int i = 0; i < msgInfoList.Count; i++)
            {
                if (msgInfoList[i] == ConstStringData.InsertStringInfo)
                {
                    result += infos.Where(info => info.insertIndex == i + 1)?.FirstOrDefault()?.info;
                    continue;
                }
                result += msgInfoList[i];
            }
            return result;
        }

        public async Task<bool> InitService()
        {
            return await Task.Run(() =>
            {
                if (string.IsNullOrEmpty(comPara))
                {
                    return false;
                }
                tcpComParaModel = JsonConvert.DeserializeObject<TcpComParaModel>(comPara);
                if (tcpComParaModel is null)
                {
                    return false;
                }
                return true;
            });
        }
        #endregion
    }
}
